package com.mtw.bbs.fileAdmin.strategy;



import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.mtw.bbs.common.core.exception.BusinessException;
import com.mtw.bbs.fileAdmin.constant.UploadStrategyEnum;
import com.mtw.bbs.fileAdmin.mapper.UploadPartMapper;
import com.mtw.bbs.fileAdmin.pojo.entity.FileEntity;
import com.mtw.bbs.fileAdmin.pojo.entity.FileShard;
import com.mtw.bbs.fileAdmin.pojo.entity.UploadPart;
import com.mtw.bbs.fileAdmin.util.FileUtilz;
import com.mtw.bbs.fileAdmin.util.MinioTemplate;
import io.minio.ComposeSource;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;

import static com.mtw.bbs.fileAdmin.constant.FAConstants.MINIO_DIR_SEPARATOE;
import static com.mtw.bbs.fileAdmin.constant.FAConstants.REDIS_PART_LOCK_KEY;

/**
 * minio文件上传
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class MinioUpload implements UploadStrategy{



    private final MinioTemplate minioTemplate;
    private final RedissonClient redissonClient;
    private final UploadPartMapper uploadPartMapper;



    @Override
    public String strategy() {
        return UploadStrategyEnum.MINIO.getCode();
    }

    /**
     * 文件上传
     */
    @SneakyThrows
    @Override
    public boolean upload(MultipartFile file, FileEntity fileInfo) {
        return minioTemplate.putObject(file.getInputStream(), null, replace(fileInfo.getInnerPath()));
    }


    /**
     * 上传
     */
    @SneakyThrows
    @Override
    public boolean simpleUpload(File file, String path){
        return minioTemplate.putObject(new FileInputStream(file), null, replace(path));
    }


    /**
     * 分片上传
     */
    @Override
    public boolean shardUpload(MultipartFile file, FileShard fileShard){
        try {

            // 生成临时分片文件存放目录
            String filePath = FileUtilz.creatShardFilePath(fileShard.getTempPath(), fileShard.getName(), fileShard.getIndex());
            // 上传分片
            minioTemplate.putObject(file.getInputStream(), null,replace(filePath));

            // 保存分片信息
            RLock partLock = redissonClient.getLock(REDIS_PART_LOCK_KEY+fileShard.getMd5()+"_"+fileShard.getIndex());
            partLock.lock();
            try {
                LambdaQueryWrapper<UploadPart> lqw = new LambdaQueryWrapper<>();
                lqw.eq(UploadPart::getShareId,fileShard.getId());
                lqw.eq(UploadPart::getPartNumber,fileShard.getIndex());
                Long count = uploadPartMapper.selectCount(lqw);
                if (count <1){
                    UploadPart part = new UploadPart();
                    part.setShareId(fileShard.getId());
                    part.setPartNumber(fileShard.getIndex());
                    uploadPartMapper.insert(part);
                }
            }finally {
                partLock.unlock();
            }
        } catch (IOException e) {
            throw new BusinessException("分片上传出错",e);
        }
        return true;
    }


    /**
     * 合并文件
     */
    @Override
    public boolean mergeFile(FileShard fileShard) {

        // 所有分片文件
        List<String> pathList = new ArrayList<>();
        for (int i = 0; i < fileShard.getShardCount(); i++) {
            // 文件路径，格式：分类文件夹 + 日期 + ( MD5_当前片 ) + 文件类型
            String path = FileUtilz.creatShardFilePath(fileShard.getTempPath(), fileShard.getName(), i);
            pathList.add(replace(path));
        }
        // 获取minio分片对象
        List<ComposeSource> chunkObjectList = minioTemplate.getChunkObjectList(null, pathList);
        // 合并文件
        boolean result = minioTemplate.composeObject(null, replace(fileShard.getPath()), chunkObjectList);
        // 合并成功之后删除对应的文件
        pathList.forEach(x->{
            minioTemplate.deleteObject(null,x);
            log.info("删除临时文件 {} 成功", x);
        });

        return result;
    }


    /**
     * 获取文件url
     * @param path
     * @return
     */
    @Override
    public String getFileUrl(String path) {
        return  minioTemplate.getPresignedObjectUrl(null,replace(path));
    }


    /**
     * 获取文件流
     */
    @Override
    public InputStream getFileStream(String path) {
        return minioTemplate.getObject(null,replace(path));
    }



    private String replace(String path){
        if (path == null){
            return null;
        }
        return path.replaceAll("\\\\",MINIO_DIR_SEPARATOE);
    }
}
